package algo_lignes_sites.data.pre_trt;

import algo_lignes_sites.data.Data_ls;
import algo_lignes_sites.data.junction.A_Junction;
import algo_lignes_sites.data.junction.JunctionREF;
import algo_lignes_sites.data.path.PathREF;
import algo_lignes_sites.data.path.SuperPath;
import algo_lignes_sites.data.pre_trt.pre_trt_path.*;
import algo_lignes_sites.data.pre_trt.pre_trt_path.simplif_topo.SimplifTopo;
import algo_lignes_sites.data.pre_trt.simplif_node.SimplifNode;
import algo_lignes_sites.params.ParamsManager_ls;
import algo_lignes_sites.params.Params_ls;
import algo_lignes_sites.params.objects.contraintesgeom.ContrainteGeomLine;
import algo_lignes_sites.params.objects.contraintesgeom.ContrainteGeomNode;
import commons.data.EchelleGeneralization;
import commons.data.Zone;
import commons.data.Zone.TypeZone;
import commons.data.category.Category;
import commons.errors.ErrorsHandler;
import commons.io.in.Data_in;
import commons.io.in.IObjetGeo_in;
import commons.io.in.Point_in;
import commons.io.in.Segment_in;
import commons.logs.Logger;
import commons.params.objects.ObjectColor;
import commons.params_appli.ParamsAppli;
import commons.spatialindex.simplegrid.SimpleGridSPI;
import commons.utils.UtilsCalcul;
import commons.utils.UtilsGeom;
import commons.utils.UtilsTime;
import commons.utils.primitive_geom.Point_d;
import commons.utils.primitive_geom.Rect_d;
import gnu.trove.set.hash.THashSet;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

@SuppressWarnings("unchecked")

public class PreTraitementInputData_ls {

	// Statistiques
	static int nbArc_in;

	static int nbSegment_init;
	static int nbSegment_apresMainFilter;
	static int nbSegment_superposes;
	static int nbSegment_apresSimplifNode;
	static int nbSegment_apresdecoupIntersection;
	static int nbSegment_apresFiltreEchelle;

	static int nbIntersect_ZoneSeg_in;

	static int nbIntersectionPath;
	static int nbJ_avantSimplifTracePath;
	static int nbJ_apresSimplifTracePath;
	static int nbJ_apresRedecoupagePath;
	
	static int nbJ_avantSimplifTopoPath;
	static int nbJ_apresSimplifTopoPath;
	static int nbJ_apresNewSimplifTracePath;
	static int nbJ_apresNewRedecoupagePath;
	
	static int nbJ_final;
	
	static int nbSimplifTopoPath;
	
	static int nbNode_init;
	static int nbNode_apresMainFilter;
	static int nbNode_apresFiltreEchelle;

	static int nbPath_final;


	// Spi adapté à la manipulation des path et des segments (les path etant traité comme des series de segment dans le SPI, pour optimiser
	//  on a besoin d'un spi de la meme taille que pour les segments indépendants
	// Index également adapté à gérer les junctions (avant et apres simplif trace path, on garde la meme taille de spi,
	//  basée sur le nombre initial de junctions)
	static SimpleGridSPI spiSegPathJunction;

	// Index plus petit, adapté pour la manipulation de nodes
	static SimpleGridSPI spiNodes;

	private static Rect_d rectEmprise;

	// ces données vont être remplies au moment de la fusion, puis mises dans le Data
	static ArrayList<JunctionREF> vJunctionREF;
	static ArrayList<JunctionREF> vNodeREF; // = sous-ensemble de vJunctionREF
	static ArrayList<PathREF> vPathREF;

	// objets supprimés lors du filtre par zone (ceux qui ont une echelle=0)
	// on garde les segments_in supprimés : leur extrémité coupée suivra celle des objets conservés
	//  (et qui bougeront pendant le traitement) pour faciliter le raccord aux frontières des zones
	static ArrayList<Segment_in> vSegment_notToBeGeneralized;
	static ArrayList<Point_in> vNode_notToBeGeneralized;

	// Params va contenir tous les paramètres entrés par l'utilisateur
	static Params_ls params;

	// Donnée temporaire utile pour savoir quelles sont les junctions qui sont intersection Seg/Zone
	//  -> elles seront snapées sur leur zone
	static ArrayList<Point_d> vPtIntersectSegZone;
	static ArrayList<Zone> vZoneOfPtIntersectSegZone;


	static public Data_ls createDataFromDirectory (JFrame frameParent, String path_RepFichierSource) {

		Data_in data_in = null;
		Data_ls data = null;

		while (data == null) {
			ErrorsHandler.reinitErrors();

			//**********************************************************************
			// A) Lecture des données dans le répertoire   Répertoire --> Data_in
			// on doit relire après chaque essai des paramètres car les segments ont
			//  peut etre déjà été découpé, par ex. : il vaut mieux repartir de zero
			//  meme si c'est plus long
			Logger.log_INFO1("");
			Logger.log_INFO1("========================================");
			Logger.log_INFO1("==== Lecture des fichiers en entrée ====");
			Logger.log_INFO1(" répertoire = " + path_RepFichierSource);
			UtilsTime.startTimer("Lecture entete fichier");

			if (data_in != null) data_in.clear(); // ceci aide grandement le GC
			//System.gc();

			data_in = Data_in.readFromDirectory_dontLoadData(path_RepFichierSource);
			UtilsTime.stopTimer("Lecture entete fichier");
			if (ErrorsHandler.isThereErrors()) {
				ErrorsHandler.addError("Impossible de lire les fichiers du répertoire : " + path_RepFichierSource);
				return null;
			}

			
			//**********************************************************************
			// B) Paramétrage de l'algorithme
			//    en entrée, les couches lues dans le répertoire de travail
			Logger.log_INFO1("");
			Logger.log_INFO1("=========================================");
			Logger.log_INFO1("==== Paramétrage de l'algorithme     ====");
			params = (Params_ls) new ParamsManager_ls(frameParent, data_in).ouvreDialogParametrage();

			if (params==null) {
				// Annulation du paramétrage
				Logger.log_INFO1("");
				Logger.log_INFO1("~~~~~~ PARAMETRAGE ANNULE PAR L'UTILISATEUR ~~~~~~");

				data_in.clear();
				data_in = null;
				//System.gc();

				break;
			}
			
			// nettoyage anciennes données
			clear();
			
			// A la sortie du paramétrage, il est temps de finir de charger les données dans les données
			// (seul le début des fichiers a été lu pour le beosin du paramétrage)
			// on ne lit que les 2 couches qui ont été demandées en paramétrage
			Logger.log_INFO1("Lecture des données géographiques des fichiers");
			UtilsTime.startTimer("Lecture donnees fichier");
			data_in.getCouche(params.nomCoucheLine).readData();
			data_in.getCouche(params.nomCoucheNode).readData();
			UtilsTime.stopTimer("Lecture donnees fichier");
			if (ErrorsHandler.isThereErrors()) {
				data_in.clear();
				data_in = null;
				// on sort, fichier erroné
				return null;
			}
			
			// Enfin, en mode debug, si demande de chargement d'un extrait seulement des données
			if (params.extraitData_use) {
				// Recherche du node
				Point_in nodeDebug = null;
				for (IObjetGeo_in node : params.coucheNode.getObjects()) {
					if (node.getAttributes_in().getValue(params.extraitData_fieldName).trim().toUpperCase().equals(params.extraitData_fieldValue.toUpperCase())) {
						nodeDebug = (Point_in) node;
						break;
					}
				}
				// on a trouvé le node sur lequel on veut zoomer
				if (nodeDebug != null) {
					
					// Rectangle que l'on  souhaite garder
					Rect_d rectDebug = new Rect_d (nodeDebug, params.extraitData_largeur);
					
					// Object filtré selon ce rectangle
					ArrayList<IObjetGeo_in> vNode_debug = new ArrayList<IObjetGeo_in>();
					ArrayList<IObjetGeo_in> vSeg_debug = new ArrayList<IObjetGeo_in>();
					
					// Filtrage
					for (IObjetGeo_in node : params.coucheNode.getObjects()) {
						if (node.getXMin()<=rectDebug.xMax && node.getXMax()>=rectDebug.xMin && node.getYMin()<=rectDebug.yMax && node.getYMax()>=rectDebug.yMin)
							vNode_debug.add(node);
					}
					for (IObjetGeo_in seg : params.coucheLine.getObjects()) {
						if (seg.getXMin()<=rectDebug.xMax && seg.getXMax()>=rectDebug.xMin && seg.getYMin()<=rectDebug.yMax && seg.getYMax()>=rectDebug.yMin)
							vSeg_debug.add(seg);
					}
					
					// Stockage des objets filtrés
					params.coucheNode.setVObj(vNode_debug);
					params.coucheLine.setVObj(vSeg_debug);
					
				} else {
					JOptionPane.showMessageDialog(null, "Attention : demande de zoom sur un node : non trouvé !! : " + params.extraitData_fieldValue + "\n -> Chargement de toutes les données");
				}
			}
				
			// Limitation éventuelle du logiciel, (réalisé apres le filtrage précédent)
			if (ParamsAppli.hasLimitationForLS && params.coucheNode.getNbObject()>ParamsAppli.maxNbNodes) {
				//JOptionPane.showMessageDialog(null, "Cette version d'Autogene est limitée à " + ParamsAppli.maxNbNodes + " au maximum");
				ErrorsHandler.addError("Cette version d'Autogene est limitée à " + ParamsAppli.maxNbNodes + " noeuds au maximum");
				return null;
			}
			
			//**********************************************************************
			// C) Traitement des données d'entrée   Data_in --> Data
			data = traiteInputData();

			if (ErrorsHandler.isThereErrors()) {
				ErrorsHandler.addError("Impossible de réaliser le pré-traitement des données d'entrée");
				ErrorsHandler.showErrors();
				ErrorsHandler.printErrors();
				// et on reboucle, sans doute une erreur de paramétrage
				clear();
				//System.gc();
			} else
				// tout est OK, on sort de la boucle
				break;

		}

		// si l'utilisateur n'a pas annulé
		if (params != null && data!=null)
			printStatsPreTraitement(data_in, data);

		clear();
		//System.gc();

		return data;
	}

	// aide le GC, cf. on a bcp de variables en statique ici et dans les divers modules de pre-traitement
	static private void clear() {
		if (spiSegPathJunction != null)
			spiSegPathJunction.clear();
		spiSegPathJunction = null;
		if (spiNodes != null)
			spiNodes.clear();
		spiNodes = null;

		if (vJunctionREF!=null)	vJunctionREF.clear();
		vJunctionREF = null;
		if (vNodeREF!=null) vNodeREF.clear();
		vNodeREF = null;
		if (vPathREF!=null) vPathREF.clear();
		vPathREF = null;
		if (vSegment_notToBeGeneralized!=null) vSegment_notToBeGeneralized.clear();
		vSegment_notToBeGeneralized = null;
	}
	
	// Renvoie null si erreur
	static private Data_ls traiteInputData() {
		try{
		Logger.log_INFO1("");
		Logger.log_INFO1("=========================================");
		Logger.log_INFO1("==== PRE-TRAITEMENT DONNEES D'ENTREE ====");
		nbArc_in = params.coucheLine.getNbAttributes();

		rectEmprise = params.coucheLine.getEmprise();
		rectEmprise.extendToRect(params.coucheNode.getEmprise());

		spiSegPathJunction = new SimpleGridSPI("Index Segments/Path/Junctions", rectEmprise, SimpleGridSPI.getTailleFor(params.coucheLine.getNbObject()));
		spiNodes = new SimpleGridSPI("Index Nodes", rectEmprise, SimpleGridSPI.getTailleFor(params.coucheNode.getNbObject()));

		UtilsTime.startTimer("TOTAL PRE-TRAITEMENT DONNEES");

		//**********************************************************************
		// Affectation des catégories aux Segment_in et Point_in
		//    -> les cétégories sont stockées dans leur Attributes
		UtilsTime.startTimer("Affectation catégories et filtrage principal");
		affecteCategories_and_MainFilter();
		UtilsTime.stopTimer("Affectation catégories et filtrage principal");

		// A l'issue du filtre principal, test si il y a des lignes et des noeuds à généraliser
		if (params.coucheLine.getNbObject()==0) {
			ErrorsHandler.addError("Après application du filtre principal, il n'y a aucune ligne à généraliser");
			return null;
		}
		if (params.coucheNode.getNbObject()==0) {
			ErrorsHandler.addError("Après application du filtre principal, il n'y a aucun noeuds à généraliser");
			return null;
		}

		// Copie des segments d'origine pour affichage ultérieur pour info (debug)
		// on le fait apres le filtre principal pour plus de lisibilité
		ArrayList<Segment_in> vSegBeforePreTraitement;
		if (Logger.isDebugLevel())
			vSegBeforePreTraitement = (ArrayList) params.coucheLine.getCopyOfVObj();
		else
			vSegBeforePreTraitement = new ArrayList<Segment_in>(0);


		//**********************************************************************
		// Suppression des objets en doublons
		UtilsTime.startTimer("Verif segments superposes");
		checkSegmentsSuperposes();
		UtilsTime.stopTimer("Verif segments superposes");

		UtilsTime.startTimer("Verif nodes superposes");
		checkNodesSuperposes();
		UtilsTime.stopTimer("Verif nodes superposes");

	
		//**********************************************************************
		// Traitement des intersections
		//  -> Detection des intersections de segments avec les zones
		UtilsTime.startTimer("Calcul intersect seg/zone");
		computeIntersect_SegZone();
		UtilsTime.stopTimer("Calcul intersect seg/zone");
		//  -> Découpage effectif des segments
		UtilsTime.startTimer("Découpage segments");
		realiseDecoupageIntersectionSegment ();
		UtilsTime.stopTimer("Découpage segments");


		//**********************************************************************
		// Affectation des zones aux Segment_in découpés et aux Point_in
		UtilsTime.startTimer("Affectation zones");
		affecteZones();
		UtilsTime.stopTimer("Affectation zones");

				
		//**********************************************************************
		// Affectation des contraintes geom, echelle de généralisation et couleur aux segment et aux point
		UtilsTime.startTimer("Affectation contraintes geom etc. aux Segment et Point");
		affecte_ContraintesGeom_RegleGeneralisation_ParamAffichage_toSegmentPoint();
		UtilsTime.stopTimer("Affectation contraintes geom etc. aux Segment et Point");
		if (ErrorsHandler.isThereErrors())
			return null;

		
		//**********************************************************************
		// Filtrage par echelle : certains objets dans certaines zones ont une échelle de généralisation = 0 -> on les retire
		UtilsTime.startTimer("Filtrage segments et nodes selon echelle generalisation");
		filtreSegmentNode_notToBeGeneralized();
		UtilsTime.stopTimer("Filtrage segments et nodes selon echelle generalisation");

		// A ce stade, il est possible qu'il n'y ait rien à généraliser, c'est à dire si tous les objets ont une echelle de de gene = 0
		if (params.coucheLine.getNbObject()==0 && params.coucheNode.getNbObject()==0) {
			ErrorsHandler.addError("Il n'y a aucune donnée à généraliser !");
			return null;
		}
		
		
		//**********************************************************************
		// Création des objet Path et Node
		// A présent, on n'utilisera plus les objets des File_in ligne et point
		UtilsTime.startTimer("Creation path et nodes");
		vPathREF = new ArrayList<PathREF>();
		vJunctionREF = new ArrayList<JunctionREF>();
		vNodeREF = new ArrayList<JunctionREF>();
		PathNodeCreator.createPathAndNodes(params, vPathREF, vJunctionREF, vNodeREF, spiSegPathJunction);
		UtilsTime.stopTimer("Creation path et nodes");


		
		//**********************************************************************
		// Les jREF étant créés, on retrouve celles issues d'un découpage seg/zone
		//   pour les snapper sur leur zone pendant le traitement
		UtilsTime.startTimer("Snap Junction/Zone");
		searchJunctionToBeSnappedToZone();
		UtilsTime.stopTimer("Snap Junction/Zone");


		//**********************************************************************
		// V18 Simplification Adaptative des Nodes
		// Phase A : Calcul de la complexité initiale des nodes
//		computeComplexiteNode_initiale();
		// Abandonné car ce calcul devait permettre de reduire la zone de simplification des nodes
		// Or il n'existe pas de critère de seuil, basé sur cette complexité, qui conviennent à tous les postes
		//  pour certains, laisser des détails est intéressant, pour d'autres ca rend la généralisation tres moche
		// On abandonne et on opte pour un paramétrage au cas par cas, par l'utilisateur, des zones d'influence
		//  des postes pour lesquels il souhaite voir le détail proche du poste


		//**********************************************************************
		// Détermine et enregistre dans les PathREF à quels NODES ils sont connectés directement et indirectement
		// Il faut faire cela avant la phase de simplification topologique car alors
		//  on pourra perdre cette info, notamment on pourra croire que certains path seraient
		//  connectés à des NODE alors que non
		// La phase d'optimisation des dissymetrie doit être appelée après, car ce traitement
		//  peut changer arbitrairement l'orientation des path
		UtilsTime.startTimer("Connection Path->NODE");
		computeConnexionIndirecteToNode();
		UtilsTime.stopTimer("Connection Path->NODE");
		

		//**********************************************************************
		// Simplification dans les NODES, on doit avoir calculé les connexions indirectes avant
		UtilsTime.startTimer("Simplification dans les nodes");
		SimplifNode.simplifyInNodes(params, vPathREF, vJunctionREF, vNodeREF, spiSegPathJunction, spiNodes);
		// on relance une analyse des connexion directe/indirecte
		// en effet, la simplification node a pu supprimer des connect_3 (cf. juste au bord de AVOI5)
		// du coup des path initialement connectés à 2 nodes via ce connect_3 se retrouvent apres simplif node connecté à un seul
		// le fait qu'il soient laissés comme connecté à 2 node peut faire des choses inesthétique
		computeConnexionIndirecteToNode();
		UtilsTime.stopTimer("Simplification dans les nodes");
		
		
		//**********************************************************************
		// Calcul des intersections entre Path et création des JunctionREF correspondantes
		UtilsTime.startTimer("Calcul intersection Path");
		int nbJavant = vJunctionREF.size();
		// On fournit vJunctionREF car l'algo va créer de nouvelle junction type INTERSECT et les stocker dedans
		int nbIntersect_4check;
		nbIntersect_4check = PathIntersector.intersectPath(vPathREF, vJunctionREF, params, spiSegPathJunction);
		int nbJapres = vJunctionREF.size();
		nbIntersectionPath = nbJapres - nbJavant;
		assert (nbIntersect_4check == nbIntersectionPath);
		UtilsTime.stopTimer("Calcul intersection Path");

//		if (ErrorsHandler.assertsEnabled) {
//			UtilsTime.startTimer("Calcul intersection Path_bis");
//			nbIntersect_4check = PathIntersector.intersectPath(vPathREF, vJunctionREF, params, spiSegPath, spiPoints);
//			assert (nbIntersect_4check==0);
//			UtilsTime.stopTimer("Calcul intersection Path_bis");
//			Logger.log_INFO1("~~~~~ DUREE INTERSECTION PATH BIS = " + UtilsTime.getStringDuree("Calcul intersection Path_bis"));
//		}


		//**********************************************************************
		// Simplification du nombre de points + ajout de points si segments trop longs
		// Il faut avoir déterminé les intersections des path avant, car la simplification
		//  de tracé cherche à ne pas rajouter d'intersections
		// Il faut avoir fait le calcul de connexion indirecte aux node avant redecoupePath, car l'algo a besoin de cette info
		UtilsTime.startTimer("Simplification tracé Path");
		nbJ_avantSimplifTracePath = vJunctionREF.size();
		
		ArrayList<PathREF> pathATraiter = vPathREF;
		int nbPasse = 8; // TODO -> INI
		for (int i=0; i<nbPasse; i++) {
			if (pathATraiter.isEmpty()) break;
			int nbJ_avant_passe = vJunctionREF.size();
			pathATraiter = SimplifTracePath.simplifyTracePath(vPathREF, pathATraiter, vJunctionREF, params, spiSegPathJunction);
			int nbJ_apres_passe = vJunctionREF.size();
			System.out.println("===> PASSE "+(i+1)+" : suppression de point = " + (nbJ_avant_passe-nbJ_apres_passe));
			System.out.println("      reste " + pathATraiter.size() + " path pas réduit complètement");
		}
		
		nbJ_apresSimplifTracePath = vJunctionREF.size();
		SimplifTracePath.redecoupPath(vPathREF, vPathREF, vJunctionREF, params, spiSegPathJunction);
		nbJ_apresRedecoupagePath = vJunctionREF.size();
		UtilsTime.stopTimer("Simplification tracé Path");

		
		//**********************************************************************
		// Simplification topologique des path
		UtilsTime.startTimer("Simplification topologique");
		ArrayList<PathREF> vPathREF_modified = new ArrayList<PathREF>();
		nbJ_avantSimplifTopoPath = vJunctionREF.size();
		nbSimplifTopoPath = SimplifTopo.simplifieTopo(params, vPathREF, vNodeREF, vJunctionREF, vPathREF_modified, spiSegPathJunction, spiNodes, false);
//		for (PathREF path : vPathREF_modified)
//			if (!vPathREF.contains(path))
//				System.out.println("/////////////// " + path);
		// On ne re-simplifie le trace que des path modifies par l'algo de simplif topo
		SimplifTracePath.simplifyTracePath(vPathREF, vPathREF_modified, vJunctionREF, params, spiSegPathJunction);
		SimplifTracePath.redecoupPath(vPathREF, vPathREF_modified, vJunctionREF, params, spiSegPathJunction);
		nbJ_apresNewRedecoupagePath = vJunctionREF.size();
		
		// On en remet une couche
		vPathREF_modified.clear();
		nbSimplifTopoPath = SimplifTopo.simplifieTopo(params, vPathREF, vNodeREF, vJunctionREF, vPathREF_modified, spiSegPathJunction, spiNodes, true);
		SimplifTracePath.simplifyTracePath(vPathREF, vPathREF_modified, vJunctionREF, params, spiSegPathJunction);
		SimplifTracePath.redecoupPath(vPathREF, vPathREF_modified, vJunctionREF, params, spiSegPathJunction);
		
		nbJ_apresNewRedecoupagePath = vJunctionREF.size();

		UtilsTime.stopTimer("Simplification topologique");




		//**********************************************************************
		// V18 Simplification Adaptative des Nodes
		// Phase B : Calcul de la complexité des nodes après simplification
		UtilsTime.startTimer("Calcul complexite node");
		CalculComplexite.computeComplexiteNodes_apresSimplif(vNodeREF, params);
		UtilsTime.stopTimer("Calcul complexite node");

		//**********************************************************************
		// Atténuation des mouvement en zones denses
		UtilsTime.startTimer("Atténuation de mouvement en zones denses");
		CalculComplexite.attenueMouvementJunctionInZoneDense(params, vPathREF, vJunctionREF, spiSegPathJunction);
		UtilsTime.stopTimer("Atténuation de mouvement en zones denses");


		//**********************************************************************
		// Regroupement des path connectés entre eux par des CONNECT_2 en SuperPath
		//    a lancer apres traiteNode et autres simplifications puisque traiteNode peut éventuellement découper des path
		UtilsTime.startTimer("Regroupement SuperPath");
		regroupeSuperPath();
		UtilsTime.stopTimer("Regroupement SuperPath");


		//**********************************************************************
		// Optimisation des dissymétries
		// Attention, l'optimisation des dissymetrie a besoin des superPath !
		UtilsTime.startTimer("Optimisation dissymétrie");
		DissymetrieOptimizer.optimizeDissymetrie(vPathREF, params, spiSegPathJunction);
		UtilsTime.stopTimer("Optimisation dissymétrie");
		if (ErrorsHandler.isThereErrors()){
			ErrorsHandler.addError("Impossible d'optimiser les dissymétries");
			return null;
		}


		//**********************************************************************
		// On met les données calculées dans le Data, qui se chargera de créer
		//  la version DER des données, quelques structures et vecteurs additionnels
		//  mise des données dans des index spatiaux etc.
		UtilsTime.startTimer("Finalisation des données");
		Data_ls data = new Data_ls(params);
		data.setData(vJunctionREF, vPathREF, vSegBeforePreTraitement, vSegment_notToBeGeneralized, vNode_notToBeGeneralized);
		UtilsTime.stopTimer("Finalisation des données");


		UtilsTime.stopTimer("TOTAL PRE-TRAITEMENT DONNEES");

		nbPath_final = vPathREF.size();
		nbJ_final = vJunctionREF.size();
		
		return data;

		} catch(StackOverflowError t) {
			// possible lors de la phase de creation de path, car pas mal de récursions (si bcp de segments connectés pour faire un path, genre une LS tres precise
			ErrorsHandler.addError("Exception StackOverflow durant le prétraitement : relancer l'application avec un paramètre -Xss plus élevé");
			return null;
		} catch(OutOfMemoryError t) {
			// normalement, pas de souci avec cette exception durant la phase de prétraitement ...
			ErrorsHandler.addError("Exception OutOfMemory durant le prétraitement : relancer l'application avec des paramètres -Xms et -Xmx plus élevés");
			return null;
		}
	}

	
	static private void affecteCategories_and_MainFilter() {
		Logger.log_INFO1("-- Affectation des catégories et filtrage principal --");

		params.coucheLine.affecteCategories(params.vCategoryLine);
		params.coucheNode.affecteCategories(params.vCategoryNode);

		// nb d'objets AVANT filtre principal
		nbSegment_init = params.coucheLine.getNbObject();
		nbNode_init = params.coucheNode.getNbObject();

		// Filtrage proprement dit
		params.coucheLine.filtreObjets_mainFilter(params.mainFilterLine);
		params.coucheNode.filtreObjets_mainFilter(params.mainFilterNode);

		// nb d'objets APRES filtre principal
		nbSegment_apresMainFilter = params.coucheLine.getNbObject();
		nbNode_apresMainFilter = params.coucheNode.getNbObject();
	}


	static private void checkSegmentsSuperposes() {
		Logger.log_INFO1("-- Vérification segments superposés --");
		nbSegment_superposes = 0;
		ArrayList<Segment_in> vSegment = (ArrayList) params.coucheLine.getObjects();

		spiSegPathJunction.clear();

		int nbSeg = vSegment.size();
		for (int i = 0; i < nbSeg; i++) {
			Segment_in seg = vSegment.get(i);
			if (spiSegPathJunction.isThisSegmentInSPI(seg)) {
				Logger.log_WARN("Segment superposé " + seg + "\n" + seg.attributes_in.toString_only10Values());
				nbSegment_superposes++;
			} else
				spiSegPathJunction.addObject(seg);
		}

		if (nbSegment_superposes > 0) {
			Logger.log_WARN(" => en tout, " + nbSegment_superposes + " segments superposés --> supprimés");

			// on remplace les objets avec ceux, uniques, du SPI
			params.coucheLine.getObjects().clear();
			params.coucheLine.getObjects().addAll((ArrayList)spiSegPathJunction.getAllObjects());
		}
	}

	static private void checkNodesSuperposes() {
		Logger.log_INFO1("-- Vérification-Suppression des nodes superposés --");
		spiNodes.clear();

		// Set des nodes superposés à supprimer
		THashSet<Point_in> setNodeToSuppr = new THashSet<Point_in>();

		ArrayList<Point_in> vNode = (ArrayList) params.coucheNode.getObjects();
		for (Point_in node : vNode) {
			if (spiNodes.isThisPointInSPI(node))
				setNodeToSuppr.add(node);
			else
				spiNodes.addPoint(node);
		}
		if (setNodeToSuppr.isEmpty())
			return;

		// Suppression effective
		Logger.log_WARN(setNodeToSuppr.size() + " nodes superposés dans les données d'entrée");
		for (Point_in node  : setNodeToSuppr) {
			vNode.remove(node);
			Logger.log_WARN(" => " + node);
			Logger.log_DEBUG1(node.getInfoDetaillee());
		}
	}

	
	// Calcule les pt d'intersection entre segments et les stocke dans les Segment_in, sans les découper
	static private void computeIntersect_SegZone() {
		Logger.log_INFO1("-- Calcul intersection Zones/Segment --");
		ArrayList<Segment_in> vSegment = (ArrayList) params.coucheLine.getObjects();

		spiSegPathJunction.clear();
		spiSegPathJunction.addObjects(vSegment);


		nbIntersect_ZoneSeg_in = 0;
		double[] tabCoord = new double[4];
		Point_d ptIntersect;
		// Pour chaque zone, on parcourt les segments alentours
		for (Zone zone : params.vZone) {
			if (zone.isZonePrincipale()) continue; // pas d'intersection possible avec la zone principale

			// Si on travaille sur un sous-ensemble de données, et qu'on a garder la definition de toutes les zones
			//  celles-ci peuvent être complètement en dehors du rectEmprise des données
			//  dans ce cas, on évite de faire la recherche d'intersection car le SPI
			//  va détecter un objet hors de son emprise et en cas de debug, cela va polluer
			//  les vraie erreurs potentielles ou des objets seraient hors du SPI
			// Donc on filtre ces cas en amont de la recherche dans le SPI
			if (!UtilsGeom.areRectIntersected(zone, rectEmprise))
				continue;

			ArrayList<Segment_in> vSegProches = (ArrayList) spiSegPathJunction.getObjectsThatMayIntersectWith(zone);

			for (Segment_in seg : vSegProches) {
				int nbIntersect=0;
				if (zone.typeZone == TypeZone.RECT)
					nbIntersect = UtilsGeom.computeIntersect_Rect_Segment(zone, seg, tabCoord);
				else if (zone.typeZone == TypeZone.ROND)
					nbIntersect = UtilsGeom.computeIntersect_Ellipse_Segment(zone, seg, tabCoord);
				else assert false;

				if (nbIntersect >= 1) {
					ptIntersect = new Point_d(tabCoord[0], tabCoord[1]);
					seg.addPtIntersection(ptIntersect);
					declarePointIntersectSegZone(zone, ptIntersect); // utilisé pour le snap junction->zone
				}
				if (nbIntersect == 2) {
					ptIntersect = new Point_d(tabCoord[2], tabCoord[3]);
					seg.addPtIntersection(ptIntersect);
					declarePointIntersectSegZone(zone, ptIntersect); // utilisé pour le snap junction->zone
				}

				nbIntersect_ZoneSeg_in += nbIntersect;
			}
		}
	}
	// Permet, par la suite, de savoir si une jREF était issue d'une intersection seg/zone
	// utilisé pour le snap junction->zone
	private static void declarePointIntersectSegZone (Zone zone, Point_d pt) {
		if (vPtIntersectSegZone == null) {
			vPtIntersectSegZone = new ArrayList<Point_d>();
			vZoneOfPtIntersectSegZone = new ArrayList<Zone>();
		}
		vPtIntersectSegZone.add(pt);
		vZoneOfPtIntersectSegZone.add(zone);
	}
	// Renvoie null si la jDER n'est pas issue d'une intersection seg/zone
	private static Zone getZoneIfIntersectSegZone(JunctionREF jREF) {
		if (vPtIntersectSegZone == null) return null;
		int nbPt = vPtIntersectSegZone.size();
		for (int i=0; i<nbPt ; i++) {
			Point_d pt = vPtIntersectSegZone.get(i);
			if (pt.equalsXY((Point_d)jREF))
				return vZoneOfPtIntersectSegZone.get(i);
		}
		return null;
	}

	// Recherche et flague les jREF issues d'un découpage seg/zone
	//  pour les snapper sur leur zone pendant le traitement
	private static void searchJunctionToBeSnappedToZone() {
		Logger.log_INFO1("-- Snap Junction sur Zone --");
		if (!ParamsAppli.getParam_b("Lignesite.SNAP_JONCTION_TO_ZONE")) {
			Logger.log_INFO1("   => Fonction désactivée dans le fichier INI de l'application");
			return;
		}

		int nbFound = 0;
		for (JunctionREF jREF : vJunctionREF) {
			// Il faut que jREF soit une extremité de son Path, ie que le path ait été coupé a cause de la zone
			// Ce test permet de ne pas rechercher l'info pour toutes les jREF
			if (jREF.isStd() && jREF.isExtremite(jREF.getMainPathContenant())) {
				
				Zone zone = getZoneIfIntersectSegZone(jREF);
				jREF.zoneIfIntersectSegZone = zone;
				if (zone != null)
					nbFound++;
			}
		}
		if (vPtIntersectSegZone!=null)
			Logger.log_INFO2(" -> nb snap = " + nbFound + " / nbIntersect seg-zone = " + vPtIntersectSegZone.size());
		else
			Logger.log_INFO2(" -> nb snap = " + nbFound + " / nbIntersect seg-zone = 0");
	}



	private static void realiseDecoupageIntersectionSegment() {
		Logger.log_INFO1("-- Decoupage des Segments --");
		// Comme certains segments vont être découpés, donc devant être supprimés
		//  du vecteur de segment du Data_in, il est plus rapide de recréer
		//  un nouveaux vecteur de segment que l'on va alimenter progressivement
		//  et on fera l'echange dans le File_in
		ArrayList vSegmentIn = params.coucheLine.getObjects();
		ArrayList vSegmentDecoupe = new ArrayList<Segment_in>(vSegmentIn.size());
		for (int i = 0; i < vSegmentIn.size(); i++) {
			Segment_in seg = (Segment_in) vSegmentIn.get(i);
			if (seg.getNbPtIntersection()==0) {
				// pas besoin de découper, on enregistre le seg directement
				vSegmentDecoupe.add(seg);
			} else {
				vSegmentDecoupe.addAll(seg.decoupe());
			}
		}
		// petite permutation de vecteurs
		params.coucheLine.setVObj(vSegmentDecoupe);

		// nb de segment APRES découpage
		nbSegment_apresdecoupIntersection = params.coucheLine.getNbObject();
	}

	
	/**
	 * Affectation des zones aux segments et aux nodes.
	 * Pour rappel, tous les objets font automatiquement partie de la zone principale par défaut
	 * car la fonction contientPt_larg() de la zone principale renvoie toujours true ...
	 */
	private static void affecteZones() {
		Logger.log_INFO1("-- Affectation des zone aux segments/nodes --");

		// 1) Affectation des zones aux segments
		ArrayList<Segment_in> vSegmentIn = (ArrayList) params.coucheLine.getObjects();
		
		for (Segment_in seg : vSegmentIn) {
			Point_d centerSeg = seg.getCenter();
			for (Zone zone : params.vZone)
				// Pour les segments, le test de présence dans la zone se fait avec le point milieu
				//  car, rappel, les segments ont déjà été découpés selon la bordure des zones
				if (zone.contientPt_larg(centerSeg))
					seg.getZones().addZone(zone);
		}

		// 2) Affectation des zones aux nodes
		ArrayList<Point_in> vPointIn = (ArrayList) params.coucheNode.getObjects();
		for (Point_in pt : vPointIn)
			for (Zone zone : params.vZone)
				if (zone.contientPt_larg(pt))
					pt.getZones().addZone(zone);
	}
	
	
	//Affectation des contraintes Geom aux Segment_in (il faut avoir affecter les zones auparavant)
	static private void affecte_ContraintesGeom_RegleGeneralisation_ParamAffichage_toSegmentPoint() {
		Logger.log_INFO1("-- Affectation des contraintes geométrique aux segment d'entree --");

		
		//***********************************************************************************************************
		// TRAITEMENT DES SEGMENTS
		int nbContrainteGeomLine = params.vContrainteGeomLine.size();
		int nbEchelleGeneralisation = params.vEchelleGeneralization_path.size();
		int nbColor = params.vColorLine.size();

		ArrayList<Segment_in> vSegment = (ArrayList) params.coucheLine.getObjects();
		int nbSeg = vSegment.size();
		
		for (int i = 0; i < nbSeg; i++) {
			Segment_in seg = vSegment.get(i);

			// 1) Contraintes géométriques
			//    On affecte dans l'ordre : chaque contrainte l'emporte sur ses valeurs qui sont renseignées
			for (int j=0 ; j<nbContrainteGeomLine ; j++) {
				ContrainteGeomLine cgl = params.vContrainteGeomLine.get(j);
				if (seg.attributes_in.isOfCategory(cgl.categoryPath))
					seg.contrainteGeomLine = ContrainteGeomLine.createContrainteGeomLine(seg.contrainteGeomLine, cgl);
			}
			if (seg.contrainteGeomLine == null) {
				ErrorsHandler.addError("Il doit y avoir une erreur dans le paramétrage des contraintes géométriques des lignes");
				ErrorsHandler.addError(seg.attributes_in.toString_only10Values(), true);
				ErrorsHandler.addError("Catégories : \n" + seg.attributes_in.getListCategoriesToString(), true);
				ErrorsHandler.addError("Ce Segment n'a pas de contrainte géométrique (épaisseur, etc.) : " + seg);
				return;
			}
			String err = seg.contrainteGeomLine.isFullyFilled();
			if (!err.isEmpty()) {
				ErrorsHandler.addError("Il doit y avoir une erreur dans le paramétrage des contraintes géométriques des lignes");
				ErrorsHandler.addError(seg.attributes_in.toString_only10Values(), true);
				ErrorsHandler.addError("Catégories : \n" + seg.attributes_in.getListCategoriesToString(), true);
				ErrorsHandler.addError("Ce Segment a des contraintes géométriques incomplètes : " + seg + " -> " + err);
				return;
			}
//			// arrivé là, pas d'erreur
//			// on s'assure que le paramètre epaisseur dissy est pas NaN, si oui, on le remplace par 0
//			// (NaN ne servait qu'à signaler qu'un ContrainteGeomLine ne devait pas écraser la valeur d'un précédent CGL rencontré pour ce segment
//			if (Double.isNaN(seg.contrainteGeomLine.epaisseur_dissymetrie_mm))
//				seg.contrainteGeomLine.epaisseurTrait_papier_mm = 0.0;

			// 2) Echelles de généralisation
			//    Rappel : une Echelle de Generalisation consiste en une echelle particulière
			//          pour une certaine catégorie d'objet dans une certaine zone
			//    On affecte dans l'ordre : la dernière règle l'emporte
			// La 1ere regle de generalisation est toujours une regle qui s'applique à tous les objets dans toutes les zones (ParamsManager s'en charge ...)
			// Au final, on est certain que tout objet aura une echelle de généralisation
			assert(params.vEchelleGeneralization_path.get(0).zone == Zone.zonePrincipale && params.vEchelleGeneralization_path.get(0).category == Category.categoryAllData);
			for (int j=0 ; j<nbEchelleGeneralisation ; j++) {
				EchelleGeneralization eg = params.vEchelleGeneralization_path.get(j);
				if (seg.attributes_in.isOfCategory(eg.category) && seg.zones.isOfZone(eg.zone))
					seg.echelleGeneralization = eg;
			}
			assert (seg.echelleGeneralization != null); // ne depend pas des params utilisateur, erreur impossible

			// 3) représentation (couleur etc.)
			for (int j=0 ; j<nbColor ; j++) {
				ObjectColor col = params.vColorLine.get(j);
				if (seg.attributes_in.isOfCategory(col.category))
					seg.color = col.color;
			}
			if (seg.color==null) {
				ErrorsHandler.addError("Il doit y avoir une erreur dans le paramétrage des règles d'affichage des lignes");
				if (Logger.isDebugLevel()) ErrorsHandler.addError(seg.attributes_in.toString_only10Values(), true);
				ErrorsHandler.addError("Ce Path n'a pas de couleur : " + seg);
				return;
			}
		}
		
		//***********************************************************************************************************
		// TRAITEMENT DES POINTS (nodes)
		ArrayList<Point_in> vPoint = (ArrayList) params.coucheNode.getObjects();
		int nbPt = vPoint.size();
		for (int i = 0; i < nbPt; i++) {
			Point_in pt = vPoint.get(i);

			// 1) Contraintes géométriques
			//    On affecte dans l'ordre : chaque contrainte l'emporte sur ses valeurs qui sont renseignées
			for (ContrainteGeomNode cgn : params.vContrainteGeomNode)
				if (pt.attributes_in.isOfCategory(cgn.categoryNode))
					pt.contrainteGeomNode = ContrainteGeomNode.createContrainteGeomNode(pt.contrainteGeomNode, cgn);
			if (pt.contrainteGeomNode == null) {
				ErrorsHandler.addError("Il doit y avoir une erreur dans le paramétrage des contraintes géométriques des Noeuds");
				ErrorsHandler.addError(pt.attributes_in.toString_only10Values(), true);
				ErrorsHandler.addError("Catégories : \n" + pt.attributes_in.getListCategoriesToString(), true);
				ErrorsHandler.addError("Ce Noeud n'a pas de contrainte géométrique (épaisseur, etc.) : " + pt);
				return;
			}
			// qques check
			String err = pt.contrainteGeomNode.isFullyFilled();
			if (!err.isEmpty()) {
				ErrorsHandler.addError("Il doit y avoir une erreur dans le paramétrage des contraintes géométriques des Noeuds");
				ErrorsHandler.addError(pt.attributes_in.toString_only10Values(), true);
				ErrorsHandler.addError("Catégories : \n" + pt.attributes_in.getListCategoriesToString(), true);
				ErrorsHandler.addError("Ce Noeud a des contraintes géométriques incomplètes : " + pt + " -> " + err);
				return;
			}
			// ce cas peut cacher une erreur de paramétrage
			//  en effet, la simplif node l'emporte sur la notion de connexité : le parcours se fait de proche en proche depuis le node, on s'arrete des qu'on sort de la ZS
			//  meme si les path parcourus ne sont plus déclarés comme connectés au node du fait d'une ZC petite
			if (pt.contrainteGeomNode.zs > pt.contrainteGeomNode.zc)
				Logger.log_WARN("Node avec ZS > ZC : " + pt.attributes_in.getValue(params.champLibelleNode));

			
			// 2) Echelle de généralisation
			// rappel : une Echelle de Generalisation consiste en une echelle particulière
			//          pour une certaine catégorie d'objet dans une certaine zone
			// La 1ere regle de generalisation est toujours une regle qui s'applique à tous les objets dans toutes les zones (ParamsManager s'en charge ...)
			// Au final, on est certain que tout objet aura une echelle de généralisation
			assert(params.vEchelleGeneralization_node.get(0).zone == Zone.zonePrincipale && params.vEchelleGeneralization_node.get(0).category == Category.categoryAllData);
			for (EchelleGeneralization eg : params.vEchelleGeneralization_node)
				if (pt.attributes_in.isOfCategory(eg.category) && pt.zones.isOfZone(eg.zone))
					pt.echelleGeneralization = eg;
			assert (pt.echelleGeneralization != null); // ne depend pas des params utilisateur, erreur impossible

			
			// 3) représentation (couleur etc.)
			for (ObjectColor col : params.vColorNode)
				if (pt.attributes_in.isOfCategory(col.category))
					pt.color = col.color;
			if (pt.color==null) {
				ErrorsHandler.addError("Il doit y avoir une erreur dans le paramétrage des règles d'affichage des Noeuds");
				if (Logger.isDebugLevel()) ErrorsHandler.addError(pt.attributes_in.toString_only10Values(), true);
				ErrorsHandler.addError("Ce Noeud n'a pas de couleur : " + pt);
				return;
			}
		}
	}
	

	/**
	 * Les segments et nodes ont leur zone, on va pouvoir déterminer ceux qui n'ont
	 * pas besoin d'etre généralisés (échelle de généralisation = 0)
	 */
	private static void filtreSegmentNode_notToBeGeneralized() {
		Logger.log_INFO1("-- Filtrage des segments et nodes a ne pas generaliser --");

		// Segments
		ArrayList<Segment_in> vSegment_origine = (ArrayList<Segment_in>) params.coucheLine.getObjects();
		ArrayList<Segment_in> vSegment_toBeGeneralized = new ArrayList<Segment_in>(vSegment_origine.size());
		vSegment_notToBeGeneralized = new ArrayList<Segment_in>();
		
		int nbSeg = vSegment_origine.size();
		for (int i=0; i<nbSeg; i++) {
			Segment_in seg = vSegment_origine.get(i);
			if (seg.echelleGeneralization.bToBeGeneralized)
				vSegment_toBeGeneralized.add(seg);
			else
				vSegment_notToBeGeneralized.add(seg);
		}
		params.coucheLine.setVObj(vSegment_toBeGeneralized);
		
		// Nodes
		ArrayList<Point_in> vNode_origine = (ArrayList<Point_in>) params.coucheNode.getObjects();
		ArrayList<Point_in> vNode_toBeGeneralized = new ArrayList<Point_in>(vNode_origine.size());
		vNode_notToBeGeneralized = new ArrayList<Point_in>();
		
		int nbNode = vNode_origine.size();
		for (int i=0; i<nbNode; i++) {
			Point_in node = vNode_origine.get(i);
			if (node.echelleGeneralization.bToBeGeneralized)
				vNode_toBeGeneralized.add(node);
			else
				vNode_notToBeGeneralized.add(node);
		}
		params.coucheNode.setVObj(vNode_toBeGeneralized);

		// nb d'objets APRES filtre
		nbSegment_apresFiltreEchelle = params.coucheLine.getNbObject();
		nbNode_apresFiltreEchelle = params.coucheNode.getNbObject();
	}


	// Détermine et enregistre dans les PathREF à quels NODES ils sont connectés directement et indirectement
	// C'est une notion locale (on ne va pas plus loin qu'un facteur de la largeur du node)
	private static void computeConnexionIndirecteToNode() {
		for (PathREF path : vPathREF)
			path.reinitNodesConnected();
		for (JunctionREF node : vNodeREF)
			node.computeTopoConnexion();

	}
	
				
	private static void regroupeSuperPath() {
		Logger.log_INFO1("-- Regroupement Path en SuperPath --");
		THashSet<PathREF> setPathATraiter = new THashSet<PathREF>();

		// On ne conserve que les path à traiter, c'est à dire ceux qui ont au moins une extrémité type CONNECT_2
		// Cas particulier des path qui reviennent sur leur point de départ, lui meme de type connect_2
		//  ceux la, on ne les traite pas
		for (int i = 0; i < vPathREF.size(); i++) {
			PathREF pathREF = vPathREF.get(i);
			if ( (pathREF.getFirstJunction().isConnect2() || pathREF.getLastJunction().isConnect2()) &&
					(pathREF.getFirstJunction() != pathREF.getLastJunction()) )
				setPathATraiter.add(pathREF);
		}
		Logger.log_INFO2(" -> regroupeSuperPath() nb Path à traiter = " + setPathATraiter.size() + " / " + vPathREF.size());

		while (setPathATraiter.size() > 0) {
			// recherche d'un path de départ, c'est à dire dont l'une des extrémités n'est pas un CONNECT_2
			PathREF pathDepart = null;
			for (PathREF pathREF : setPathATraiter)
				if (!pathREF.getFirstJunction().isConnect2() || !pathREF.getLastJunction().isConnect2()) {
					pathDepart = pathREF;
					break;
				}
			if (pathDepart == null) {
				Logger.log_INFO2(" -> regroupeSuperPath() : il reste " + setPathATraiter.size() + " dans le set en fin de traitement !");
				// on sort du while
				break;
			}

			// A partir de ce point de départ, on progresse de proche en proche
			SuperPath superPath = new SuperPath();
			A_Junction j1;
			if (!pathDepart.getFirstJunction().isConnect2())
				j1 = pathDepart.getFirstJunction();
			else
				j1 = pathDepart.getLastJunction();
			traitePathConnect2ToThisOne_fromThisJunction(pathDepart, j1, superPath, setPathATraiter);
		}

		// Enfin, tous les Path qui n'ont pas encore eu de super path (path qui ne sont connectés à d'autres
		//  que par des points multiples (nodes, CONNECT_3, etc.)
		// on leur affecte un SuperPath unique pour eux (numSuperPAth < 0 pour être plus facilement reconnaissable)
		for (PathREF pathREF : vPathREF)
			if (pathREF.getSuperPath()==null)
				pathREF.setSuperPath(new SuperPath(true));
	}

	private static void traitePathConnect2ToThisOne_fromThisJunction (PathREF pathREF, A_Junction j1, SuperPath superPath, THashSet<PathREF> setPathATraiter) {
		// Il est possible que le pathREF ne soit pas dans le set de traitement
		// cas d'un path qui revient sur lui-même et dont les 2 extrémités sont égales et de type connect_2
		//assert setPathATraiter.contains(pathREF) : "le path " + pathREF + " ne fait plus partie du set : \n" + pathREF.getInfoDetaillee();
		if (!setPathATraiter.contains(pathREF))
			return;

		setPathATraiter.remove(pathREF);
		pathREF.setSuperPath(superPath);

		// on poursuit au niveau de l'autre extrémité
		A_Junction j2;
		if (pathREF.getFirstJunction() == j1)
			j2 = pathREF.getLastJunction();
		else
			j2 = pathREF.getFirstJunction();

		if (j2.isConnect2()) {
			PathREF pathSuivant;
			if (j2.getPathContenantNum(0) == pathREF)
				pathSuivant = (PathREF) j2.getPathContenantNum(1);
			else
				pathSuivant = (PathREF) j2.getPathContenantNum(0);

			// appel récursif sur le path suivant
			traitePathConnect2ToThisOne_fromThisJunction (pathSuivant, j2, superPath, setPathATraiter);
		}
		// Sinon le parcourt est terminé
	}


	//**************************************************************************
	//**************************************************************************


	static private void printStatsPreTraitement(Data_in data_in, Data_ls data) {
		Logger.log_INFO1("");
		Logger.log_INFO1("=========================================================");
		Logger.log_INFO1("=========================================================");
		Logger.log_INFO1("** Statistiques du pre-traitement des données d'entrée **");
		Logger.log_INFO1("=========================================================");
		Logger.log_INFO1("=========================================================");
		Logger.log_INFO1(data_in.path_RepFichierSource);

		Logger.log_INFO1("");
		data_in.printStatsGlobales();

		Logger.log_INFO2("");
		Logger.log_INFO2("-- Préparation des données :");
		Logger.log_INFO2(" nb. arc en entrée                  : " + nbArc_in);
		Logger.log_INFO2(" nb. segment en entrée              : " + nbSegment_init);
		Logger.log_INFO2(" nb. segment après filtre principal : " + nbSegment_apresMainFilter);
		Logger.log_INFO2(" nb. segment après simplif. node    : " + nbSegment_apresSimplifNode);
		Logger.log_INFO2(" nb. pt intersect zone/seg          : " + nbIntersect_ZoneSeg_in);
		Logger.log_INFO2(" nb. segment après découp. intersect: " + nbSegment_apresdecoupIntersection);
		Logger.log_INFO2(" nb. segment après filtre par zone  : " + nbSegment_apresFiltreEchelle);
		Logger.log_INFO2("");
		Logger.log_INFO2(" nb. point intersection entre path  : " + nbIntersectionPath);
		Logger.log_INFO2(" nb. jonctions avant simplif. path  : " + nbJ_avantSimplifTracePath);
		Logger.log_INFO2(" nb. jonctions après simplif. path  : " + nbJ_apresSimplifTracePath);
		Logger.log_INFO2(" nb. jonctions après redecoup. path : " + nbJ_apresRedecoupagePath);
		Logger.log_INFO2("");
		Logger.log_INFO2("Simplification topologique des path :");
		Logger.log_INFO2("Au TOTAL :");
		Logger.log_INFO2(" nb. simplif realisees              : " + SimplifTopo.getNbPatternRealise() + "  (delta junctions : " + (nbJ_apresSimplifTopoPath-nbJ_avantSimplifTopoPath) + ")");
		Logger.log_INFO2(" nb. pattern testes                 : " + SimplifTopo.getNbPatternTeste());
		Logger.log_INFO2(" nb. jonctions après redecoup. path : " + nbJ_apresNewRedecoupagePath);
		Logger.log_INFO2("   -> delta global simplif topo     : " + (nbJ_apresNewRedecoupagePath-nbJ_avantSimplifTopoPath));
		Logger.log_INFO2("Dont ...");
		Logger.log_INFO2("PATTERN BoucleDansNode :");
		Logger.log_INFO2(" nb. simplif realisees              : " + SimplifTopo.nbPatternRealise_BoucleDansNode);
		Logger.log_INFO2("PATTERN PetitPath :");
		Logger.log_INFO2(" nb. simplif realisees              : " + SimplifTopo.nbPatternRealise_PetitPath);
		Logger.log_INFO2("PATTERN 3->1 :");
		Logger.log_INFO2(" nb. simplif realisees              : " + SimplifTopo.nbPatternRealise_3PtMultTo1);
		Logger.log_INFO2(" nb. pattern testes                 : " + SimplifTopo.nbPatternTeste_3PtMultTo1);
		Logger.log_INFO2("PATTERN 2->1 :");
		Logger.log_INFO2(" nb. simplif realisees              : " + SimplifTopo.nbPatternRealise_2PtMultTo1);
		Logger.log_INFO2(" nb. pattern testes                 : " + SimplifTopo.nbPatternTeste_2PtMultTo1);
		Logger.log_INFO2("");
		Logger.log_INFO2(" nb. jonctions dont mvt. atténué car zone dense : " + CalculComplexite.nbJunctionAttenuee);
		Logger.log_INFO2("");
		Logger.log_INFO2(" nb. node en entrée                 : " + nbNode_init);
		Logger.log_INFO2(" nb. nodes après filtre principal   : " + nbNode_apresMainFilter);
		Logger.log_INFO2(" nb. nodes après filtre par zone    : " + nbNode_apresFiltreEchelle);
		Logger.log_INFO2("");
		Logger.log_INFO2(" nb. jonction final : " + nbJ_final);
		Logger.log_INFO2(" nb. path final     : " + nbPath_final);

		Logger.log_INFO2("");
		Logger.log_INFO2("-- Répartition des catégories des lignes :");
		Logger.log_INFO2(" Sur " + nbArc_in + " arc en entrée :");
		Logger.log_INFO2(params.coucheLine.getStatCategory(params.vCategoryLine));
		Logger.log_INFO2("");
		Logger.log_INFO2("-- Répartition des catégories des noeuds :");
		Logger.log_INFO2(" Sur " + nbNode_init/*params.coucheNode.getNbObject()*/ + " noeuds en entrée :");
		Logger.log_INFO2(params.coucheNode.getStatCategory(params.vCategoryNode));

		Logger.log_DEBUG1("");
		spiSegPathJunction.printStats();
		Logger.log_DEBUG1("");
		spiNodes.printStats();

		data.printStats();		

		Logger.log_INFO1("");
		Logger.log_INFO1("=========================================================");
		Logger.log_INFO1("=== RAPPORT DE PERFORMANCE pour la création des données ===");
		Logger.log_INFO2("Taille index Intersect : " + spiSegPathJunction.getNbDecoupage());
		Logger.log_INFO2("Taille index Fusion    : " + spiNodes.getNbDecoupage());
		Logger.log_INFO2(UtilsTime.getStringDuree("Lecture entete fichier"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Lecture donnees fichier"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Affectation catégories et filtrage principal"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Verif segments superposes"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Calcul intersect seg/zone"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Découpage segments"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Affectation contraintes geom etc. aux Segment et Point"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Filtrage segments et nodes selon echelle generalisation"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Creation path et nodes"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Snap Junction/Zone"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Connection Path->NODE"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Simplification dans les nodes"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Calcul intersection Path"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Simplification tracé Path"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Simplification topologique"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Calcul complexite node"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Atténuation de mouvement en zones denses"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Optimisation dissymétrie"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Regroupement SuperPath"));
		Logger.log_INFO2(UtilsTime.getStringDuree("Finalisation des données"));
		Logger.log_INFO1("=> DUREE TOTALE : " + 
				UtilsCalcul.round(UtilsTime.getDuree("TOTAL PRE-TRAITEMENT DONNEES") +
								  UtilsTime.getDuree("Lecture entete fichier") +
								  UtilsTime.getDuree("Lecture donnees fichier"), 2));

		Logger.log_INFO1("=========================================================");
		Logger.log_INFO1("=========================================================");
		Logger.log_INFO1("");

//		int max = -1;
//		for (PathREF p : vPathREF)
//			max = Math.max(max, p.getVJunction().size());
//		System.out.println("LONGEST PATH : " + max);

	}


}
